package com.gw2.gw2world.web.server.cron;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.hibernate.Session;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Repository;

import com.gw2.gw2world.web.server.Downloader;
import com.gw2.gw2world.web.server.HibernateUtils;
import com.gw2.gw2world.web.server.JsonDecoder;
import com.gw2.gw2world.web.server.model.Event;
import com.gw2.gw2world.web.server.model.Item;
import com.gw2.gw2world.web.server.model.MiniMap;
import com.gw2.gw2world.web.server.model.Translation;
import com.gw2.gw2world.web.server.model.Translation.Type;

@Repository("updateJob")
public class UpdateChecker {
	private static Properties properties;
	private static int currentVersion=0;
	private static Session session;
	private static final String[] LANGUAGES = new String[]{"en","fr","de","es"};
	private static String result;
	
	@Scheduled(cron = "0 5 * * * ?")
	public void execute() {
		try {
			 result=Downloader.download("https://api.guildwars2.com/v1/build.json");
		} catch (Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").error("Unable to get game build from GW2 API");
			return;
		}
		try {
			int version=JsonDecoder.getInstance().decodeBuild(result);
			if(version!=getSavedBuildVersion()){ //check version and download if different
				//session=HibernateUtils.getSessionFactory().getCurrentSession();
				session=HibernateUtils.getSessionFactory().openSession();
				org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").info("Getting servers...");
				getServers();
				org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").info("Getting maps...");
				getMaps();
				org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").info("Getting events...");
				getEvents();
				//get items
				getItems();
				saveBuildVersion(version);
			}
		} catch(Exception e) {
			e.printStackTrace();
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").error("Unable to parse game build");
		} finally {
			session.close();
		}
	}
	
	private int getSavedBuildVersion() {
		currentVersion=0;
		properties = new Properties();
		File f=new File("config.properties");
		try {
			//load configuration file
			if(f.exists()) {
				FileReader fr=new FileReader(f);
				properties.load(fr);
				currentVersion=Integer.parseInt(properties.getProperty("gameBuild", "0"));
				fr.close();
			}
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").error("Unable to get game build in configuration file");
		} finally {}
		return currentVersion;
	}
	private void saveBuildVersion(int version) {
		properties = new Properties();
		File f=new File("config.properties");
		try {
			//load configuration file
			if(f.exists()) {
				properties.setProperty("gameBuild", String.valueOf(version));
				OutputStream out = new FileOutputStream(f);
				properties.store(out, "");
			}
		} catch(Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").error("Unable to save game build in configuration file");
		} finally {}
	}
	/** Get the server from GW2 API from the following url: https://api.guildwars2.com/v1/world_names.json?lang=XX
	 * where XX is the locale code (en or fr or de or es).
	 * It saves servers ID in the database (table: Server).
	 */
	private void getServers() {
		final String URL="https://api.guildwars2.com/v1/world_names.json?lang=";
		Translation t;
		Map<Integer, String> servers;
		try {
			 //save translation in Hibernate and JSON file /res/gen/servers_xx.json where xx is the locale code (en/fr/de/es)
			session.beginTransaction();
			 for(int i=0; i<LANGUAGES.length; i++) {
				 result=Downloader.download(URL+LANGUAGES[i]);
				 servers=JsonDecoder.getInstance().decodeServerNames(result);
				 for(Integer id : servers.keySet()) {
					 t=new Translation(id.toString(),LANGUAGES[i],servers.get(id),Type.SERVER);
					 session.saveOrUpdate(t);
				 }
				 servers.clear();
			 }
			 session.getTransaction().commit();
			 
		} catch (Exception e) {
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").error("Unable to get servers from GW2 API");
			return;
		}
	}
	/** Get map names first and then map details. Information are save to DB.
	 */
	private void getMaps() {
		final String URL="https://api.guildwars2.com/v1/map_names.json?lang=";
		Translation t;
		Map<Integer, String> maps;
		try {
			//get map names
			for(int i=0; i<LANGUAGES.length; i++) {
				result=Downloader.download(URL+LANGUAGES[i]);
				maps=JsonDecoder.getInstance().decodeMapNames(result);
				session.beginTransaction();
				for(Integer id : maps.keySet()) {
					t=new Translation(id.toString(), LANGUAGES[i], maps.get(id), Type.MAP);
					session.saveOrUpdate(t);
				}
				session.getTransaction().commit();
			}
			session.flush();
			session.clear();
			//get map details
			result=Downloader.download("https://api.guildwars2.com/v1/maps.json");
			session.beginTransaction();
			boolean destailInserted=false;
			java.util.List<Translation> l=new java.util.ArrayList<Translation>();
			for(int i=0; i<LANGUAGES.length; i++) {
				for(MiniMap mm : JsonDecoder.getInstance().decodeMapDetails(result)) {
					t=new Translation(String.valueOf(mm.getContinent_id()), LANGUAGES[i], mm.getContinent_name(), Type.CONTINENT);
					if(!l.contains(t)) {
						session.saveOrUpdate(t);
						l.add(t);
					}
					t=new Translation(String.valueOf(mm.getRegion_id()), LANGUAGES[i], mm.getRegion_name(), Type.REGION);
					if(!l.contains(t)) {
						session.saveOrUpdate(t);
						l.add(t);
					}
					if(!destailInserted)
						session.saveOrUpdate(mm);
				}
				l.clear();
				destailInserted=true;
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").error("Unable to get maps from GW2 API");
			return;
		}
	}
	/** Get event names and details
	 */
	private void getEvents() {
		final String URL="https://api.guildwars2.com/v1/event_names.json?lang=";
		Translation t;
		Map<String, String> translations;
		java.util.List<Event> events;
		try {
			//get event names
			for(int i=0; i<LANGUAGES.length; i++) {
				result=Downloader.download(URL+LANGUAGES[i]);
				translations=JsonDecoder.getInstance().decodeEventNames(result);
				int n=0;
				session.beginTransaction();
				for(String id : translations.keySet()) {
					t=new Translation(id, LANGUAGES[i], translations.get(id), Type.EVENT);
					session.saveOrUpdate(t);
					n++;
					if(n%100==0) { 
						session.getTransaction().commit();
						session.beginTransaction();
					}
				}
				session.getTransaction().commit();
				translations.clear();
			}
			//get event details
			result=Downloader.download("https://api.guildwars2.com/v1/event_details.json");
			session.beginTransaction();
			events=JsonDecoder.getInstance().decodeEventDetails(result);
			//System.out.println("Event count: "+events.size());
			for(Event e : events) {
				if(e.getMap_id()==922) continue; //skip this one because it is not retrieve from API in maps.json
				//System.out.println(e.getId()+" "+String.valueOf(e.getLevel())+" "+String.valueOf(e.getMap_id()));
				session.saveOrUpdate(e);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			org.apache.log4j.Logger.getLogger("com.gw2.gw2world.web.server.cron").error("Unable to get events from GW2 API");
			return;
		}
	}
	private void getItems() {
		Translation t;
		boolean itemSaved;
		try {
			result=Downloader.download("https://api.guildwars2.com/v1/items.json");
			Set<Integer> ids=new HashSet<Integer>(JsonDecoder.getInstance().getItemIdsOrRecipeIds(result));
			List<?> l=session.createSQLQuery("SELECT * FROM v_item_complete").list();
			for (java.util.Iterator<?> iter = l.iterator(); iter.hasNext();) {
				Integer id = (Integer) iter.next();
				if(ids.contains(id)) ids.remove(id);
			}
			for(Integer id : ids) {
				itemSaved=false;
				session.beginTransaction();
				for(int i=0;i<LANGUAGES.length; i++){
					result=Downloader.download("https://api.guildwars2.com/v1/item_details.json?item_id="+id.toString()+"&lang="+LANGUAGES[i]);
					Item item=JsonDecoder.getInstance().getItemDetail(result);
					t=new Translation(String.valueOf(item.getId()), LANGUAGES[i], item.getName(), Type.ITEM);
					session.saveOrUpdate(t);
					if(!itemSaved) {
						session.saveOrUpdate(item);
						itemSaved=true;
					}
					
				}
				session.getTransaction().commit();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
